package com.inetools.gwetons.client;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.ListIterator;
import java.util.MissingResourceException;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer;
import com.google.gwt.user.client.DOM;

/**
 * Factory class of ResourceDeliver interface
 * In using this, insert the following style tag and javascript script tag either in your app's 
 * .gwt.xml file or the host html file's head tag:
<style type="text/css">
.ResourceDeliver {display:block;}
</style>

<script type="text/javascript">
//<![CDATA[
if ( document.styleSheets) {
	var baseStyleSheets = document.styleSheets[ 0];
	if ( baseStyleSheets) {
		if ( baseStyleSheets.addRule) { // for IE
			baseStyleSheets.addRule( ".ResourceDeliver", "display:none");
		}
		else if ( ( baseStyleSheets.insertRule) && ( baseStyleSheets.cssRules)) {
			var	rulesLength = baseStyleSheets.cssRules.length;
			baseStyleSheets.insertRule( ".ResourceDeliver {display:none;}", rulesLength);
		}
	}
}
//]]> 
</script>
 * 
 * The purpose of this class is to help to create SEO friendlier internationalized GWT app. 
 * It is highly recommended to use this class only for static text (or image) of HTML content.
 * For the dynamic contents such as error message, pop-up dialog message or any others, 
 * it is recommended to use GWT's conventional internationalization way like Constants class 
 * or Messages class which use property file, or Dictionary class. It's because using GWT's 
 * conventional internationalization way (like Constants class or Messages class which use property 
 * file) is quicker, although this class optimizes by using dictionary, still this class takes more 
 * time to load resource in its fist access, and preferable to avoid showing the dynamic resources 
 * all the time when not-javascript-enabled browser accesses the page since often times dynamic 
 * content make sense only when the specific circumstance occurs.
 * It is highly recommended avoiding falling back to ultimate fall-back resource, since it takes more 
 * time.
 * Note: 
 * This does use RegExp object of underlying javascript engine of browser for extracting language 
 * code and country code from ID and lang CSS style rule.  
 * Thereby, when 
 * @author ay
 */
// TODO I may change not to use RegExp object for better cross browser support
public class ResourceDeliver {
	
	protected static boolean genericDefaultLocaleFlag = false;
	protected static String localeNameStr = 
		LocaleInfo.getCurrentLocale().getLocaleName().replace( " ", "");
	static {
		if ( localeNameStr.equalsIgnoreCase( "default")) genericDefaultLocaleFlag = true; 
GWT.log( "localeNameStr = " + localeNameStr, null);
	};
	protected static String localeNameStrInLowerCase = localeNameStr.toLowerCase();
	
	public static enum ContentAccessMeans {
		InnerText,
		InnerHTML
	}
	/**
	 * Used to specify how to grab resource contents; either by using getInnerText or getInnerHTML.
	 * Default is using getInnerText
	 */
	protected static ContentAccessMeans contentAccessMeans = ContentAccessMeans.InnerText;
	public static ContentAccessMeans getContentAccessMeans() {
		return contentAccessMeans;
	}
	public static void setContentAccessMeans( ContentAccessMeans contentAccessMeans) {
		ResourceDeliver.contentAccessMeans = contentAccessMeans;
	}


	private ResourceDeliver() {
	}
	protected static ResourceDeliver _instance = new ResourceDeliver();

	
/* Changed to get localeNameStr value at instantiation
	protected String getCurrentLocaleCode() {
		String localeNameStr = LocaleInfo.getCurrentLocale().getLocaleName().replace( " ", "");
GWT.log( "localeNameStr = " + localeNameStr, null);
		if ( localeNameStr.equalsIgnoreCase( "default")) {
			// Found out that getAnnotation method is not supported in GWT
			// ResourceDefaultLocale defaultLocale = 
				// (ResourceDefaultLocale)this.getClass().getAnnotation( ResourceDefaultLocale.class);
			// String defaultLocaleStr = defaultLocale.value();
GWT.log( "defaultLocaleStr = \"" + defaultLocaleStr + "\"", null);
			// localeNameStr = defaultLocaleStr.replace( " ", "");
		}
		return localeNameStr;
	} // protected String getCurrentLocaleCode()
*/
	
	protected String keyValidator( String key) throws IllegalArgumentException {
		String keyCopy = key.trim();
		if ( ( keyCopy == null) || "".equals( keyCopy)) {
			throw new IllegalArgumentException( "The key argument needs to be non-empty string.");
		}
		return keyCopy;
	}
	protected boolean isKeyWithLangCode( String key) {
		JavaScriptObject langCountryCodeRegExpObj = 
			RegExpJsni.getRegExp( "\\s*_\\s*[a-zA-z]{2}\\s*_\\s*[a-zA-z]{2}$", true, false);
		JavaScriptObject langCodeRegExpObj = 
			RegExpJsni.getRegExp( "\\s*_\\s*[a-zA-z]{2}$", true, false);
		if ( RegExpJsni.test( langCountryCodeRegExpObj, key)) {
			return true;
		}
		else {
			if ( RegExpJsni.test( langCodeRegExpObj, key)) {
				return true;
			}
		}
		return false;
	} // protected boolean isKeyWithLangCode( String key)
	
	/**
	 * 
	 * @param wrapperElement
	 * @param key: just used for exception message
	 * @return
	 * @throws MissingResourceException
	 */
	protected Element getFirstDivElement( Element wrapperElement, String key) throws MissingResourceException {
		if ( !wrapperElement.hasChildNodes()) {
			throw new MissingResourceException( "Could not locate the resource for default locale.", null, key);
		}
		NodeList<Node> nodeList = wrapperElement.getChildNodes();
		for( int nodeIndex = 0; nodeIndex < nodeList.getLength(); nodeIndex++) {
			Node genericDefaultResourceDivNode = nodeList.getItem( nodeIndex);
			if ( !genericDefaultResourceDivNode.getNodeName().equalsIgnoreCase( "div")) continue;
			return Element.as( genericDefaultResourceDivNode);
		} // for
		throw new MissingResourceException( "Could not locate the resource for default locale.", null, key);
	} // protected Element getResourceElement( Element wrapperElement, String key)

	protected Element getWrapperDomElement( String key, boolean keyWithLangCodeFlag) throws MissingResourceException {
		String wrapperDivId = key.trim();
		if ( keyWithLangCodeFlag) {
			RegExpJsniIntf langCountryCodeRegExpObj 
			= new RegExpJsni( "(\\s*_\\s*[a-zA-z]{2}\\s*){1,2}$", true, false, false); 
			langCountryCodeRegExpObj.test( key);
			wrapperDivId = langCountryCodeRegExpObj.getLeftContext().trim();
		}
		
		Element wrapperDOMElement = DOM.getElementById( wrapperDivId);
		if ( wrapperDOMElement == null) {
			throw new MissingResourceException( 
					"Could not find the resource group div node identified by ID:" + wrapperDivId, 
					null, wrapperDivId);
		}
		else if ( !wrapperDOMElement.hasChildNodes()) {
			throw new MissingResourceException( 
					"Could not find any possible resources under " + wrapperDivId + " DOM node", 
					null, wrapperDivId);
		}
		
		return wrapperDOMElement;
	} // protected Element getWrapperDomElement( String key, boolean keyWithLangCodeFlag)

	/*
	 * Note: 
	 * 	About ultimate fall back (default fall back), it is allowed for the div DOM node for 
	 *  default fall back (which will be the first child div DOM node) to have neither ID nor 
	 *  lang CSS style attribute. Thereby, in such case, the div DOM node for default fall back 
	 *  may not be ended up into languageFallbackLadderLinkedHashMap
	 */
	protected LinkedHashMap<String, Element> createFallBackLanguageLadder( 
			String key, boolean keyWithLangCodeFlag) {
		// Before being saved into language code ladder for fall back, each key value will be 
		// transformed to lower case characters.
		// Only keys in language code ladder for fall back are intentionally converted to lower 
		// case. In other statements, such as storing key into ResourceDeliverDictionary or 
		// querying DOM div node by using key as suffix of div node ID or value of lang attribute, 
		// key will not be converted intentionally to lower case, in order to comply to XML syntax and 
		// avoid additional consideration for handling character case of user input
		LinkedHashMap<String, Element> languageFallbackLadderLinkedHashMap = 
			new LinkedHashMap<String, Element>();

		RegExpJsniIntf langCountryCodeRegExpObj;
		RegExpJsniIntf langCodeRegExpObj;
		if ( keyWithLangCodeFlag) {
			langCountryCodeRegExpObj 
			= new RegExpJsni( "\\s*_\\s*[a-zA-z]{2}\\s*_\\s*[a-zA-z]{2}$", true, false, false); 
			if ( langCountryCodeRegExpObj.test( key)) {
				String langCountryCodeStr = langCountryCodeRegExpObj.getLastMatch();
GWT.log( "langCountryCodeStr = \"" + langCountryCodeStr + "\"", null);
				langCountryCodeStr = langCountryCodeStr.replaceAll( " ", "");
				// Keep key (lang code and country code) in languageFallbackLadderLinkedHashMap in lower case character.
				langCountryCodeStr = langCountryCodeStr.substring( 1).toLowerCase();
				// Add combination string of language code and country code languageFallbackLadderLinkedHashMap
				languageFallbackLadderLinkedHashMap.put( langCountryCodeStr, null);
				// Add language code string to languageFallbackLadderLinkedHashMap
				String langCode = langCountryCodeStr.substring( 0, langCountryCodeStr.lastIndexOf( "_"));
				languageFallbackLadderLinkedHashMap.put( langCode, null);

				if ( !genericDefaultLocaleFlag) {
					if ( !languageFallbackLadderLinkedHashMap.containsKey( localeNameStrInLowerCase)) {
						languageFallbackLadderLinkedHashMap.put( localeNameStrInLowerCase, null);
						
						langCodeRegExpObj 
						= new RegExpJsni( "\\s*_\\s*[a-zA-z]{2}\\s*$", true, false, false); 
						if ( langCodeRegExpObj.test( localeNameStrInLowerCase)) {
							String localeLangCode = langCodeRegExpObj.getLeftContext();
							languageFallbackLadderLinkedHashMap.put( localeLangCode, null);
						}
					}
				}
				
			}
			else { // keyCopy doesn't have "_xx_YY" suffix
				langCodeRegExpObj 
				= new RegExpJsni( "\\s*_\\s*[a-zA-z]{2}$", true, false, false);
				if ( langCodeRegExpObj.test( key)) {
					String langCode 
					= langCodeRegExpObj.getLastMatch().replaceAll( " ", "");
					langCode = langCode.substring( langCode.lastIndexOf( "_") + 1);
					languageFallbackLadderLinkedHashMap.put( langCode.toLowerCase(), null);

					if ( !genericDefaultLocaleFlag) {
						langCountryCodeRegExpObj 
						= new RegExpJsni( "\\s*_\\s*[a-zA-z]{2}\\s*$", true, false, false); 
						boolean localeWithLangCountryComboFlag = false;
						
						String localeLangCode = localeNameStrInLowerCase;
						if ( langCountryCodeRegExpObj.test( localeNameStrInLowerCase)) {
							localeWithLangCountryComboFlag = true;
							localeLangCode = langCountryCodeRegExpObj.getLeftContext();
						}
						
						if ( !languageFallbackLadderLinkedHashMap.containsKey( localeLangCode)) {
							languageFallbackLadderLinkedHashMap.put( localeNameStrInLowerCase, null);
							if ( localeWithLangCountryComboFlag) {
								languageFallbackLadderLinkedHashMap.put( localeLangCode, null);
							}
						}
					}
				}
			}
		}
		else { // keyWithLangCodeFlag is false
			if ( !genericDefaultLocaleFlag) {
				languageFallbackLadderLinkedHashMap.put( localeNameStrInLowerCase, null);
				langCodeRegExpObj 
				= new RegExpJsni( "\\s*_\\s*[a-zA-z]{2}$", true, false, false); 
				if ( langCodeRegExpObj.test( localeNameStrInLowerCase)) {
					String localeLangCode = langCodeRegExpObj.getLeftContext();
					languageFallbackLadderLinkedHashMap.put( localeLangCode, null);
				}
			}
		}
		
		return languageFallbackLadderLinkedHashMap;
	} // protected LinkedHashMap<String, String> createFallBackLanguageLadder( String key, boolean keyWithLangCodeFlag)

	// Save DOM element for each key in language code ladder for fall back
	protected Element fillDomElementInLangLadder( 
			Element wrapperDOMElement, LinkedHashMap<String, Element> languageFallbackLadderLinkedHashMap) {
		Element ultimateFallBackElement = null;
		NodeList<Node> nodeList = wrapperDOMElement.getChildNodes();
		ArrayList<String> arrayList = new ArrayList<String>( languageFallbackLadderLinkedHashMap.keySet());
		for( int nodeIndex = 0; nodeIndex < nodeList.getLength(); nodeIndex++) {
			Node node = nodeList.getItem( nodeIndex);
			if ( node.getNodeType() != 1) continue;
			if ( !node.getNodeName().equalsIgnoreCase( "div")) continue;
			
			Element element = Element.as( node);

			if ( ultimateFallBackElement == null) ultimateFallBackElement = element;
			
			String langCountryCodeOfElement = element.getId(); 	// ID has highest priority
			if ( ( langCountryCodeOfElement != null) && !"".equals( langCountryCodeOfElement)) {
				RegExpJsniIntf langCountryCodeRegExpObj 
				= new RegExpJsni( "(\\s*_\\s*[a-zA-z]{2}\\s*){1,2}$", true, false, false); 
				if ( langCountryCodeRegExpObj.test( langCountryCodeOfElement)) {
					langCountryCodeOfElement = langCountryCodeRegExpObj.getLastMatch().replaceAll( " ", "");
					langCountryCodeOfElement = langCountryCodeOfElement.substring( 1);
				}
				else {
					langCountryCodeOfElement = null;
				}
			}
			if ( ( langCountryCodeOfElement == null) || "".equals( langCountryCodeOfElement)) {
//TODO Must check the correct value syntax for lang DOM attribute. It may not be same with language code and country code which I'm thinking of
// Shall I check language code and country code by using RegExpJsni here too? 
				langCountryCodeOfElement = element.getLang(); 	// lang DOM attribute has second highest priority
				if ( ( langCountryCodeOfElement == null) || ( "".equals( langCountryCodeOfElement))) {
					// Custom language property of CSS style rule has lowest priority
					langCountryCodeOfElement = element.getStyle().getProperty( "language");
				}
			}
			if ( ( langCountryCodeOfElement != null) && ( !"".equals( langCountryCodeOfElement))) {
				langCountryCodeOfElement = langCountryCodeOfElement.toLowerCase();
				
				if ( languageFallbackLadderLinkedHashMap.containsKey( langCountryCodeOfElement)) {
					languageFallbackLadderLinkedHashMap.put( langCountryCodeOfElement, element);
					int localeCodeIndex = arrayList.indexOf( langCountryCodeOfElement);
					if ( localeCodeIndex > -1) {
						ListIterator<String> listIterator = arrayList.listIterator( localeCodeIndex + 1);
						while( listIterator.hasNext()) {
							languageFallbackLadderLinkedHashMap.remove( listIterator.next());
							listIterator.remove();
						} // while
					}
					else {
	GWT.log( "ERROR: Duplicate locale code: " + langCountryCodeOfElement + " among div IDs under " + wrapperDOMElement.getId() + " DOM node", null);
					}
				}
				
			}
				
		} // for
		
		return ultimateFallBackElement;
	} // protected void fillDomElementInLangLadder( Element wrapperDOMElement, LinkedHashMap<String, Element> languageFallbackLadderLinkedHashMap)
	
	public static String getValue( String key, boolean asEscaped) 
	throws IllegalArgumentException, MissingResourceException {
		String keyCopy = _instance.keyValidator( key);

		/*
		 * <div id='stringResource1' class="STRImporter">
		 * 
		 * <!-- First child div node is (string) resource for default fall back  -->
		 * <div>String resource for default fall back</div>
		 * 
		 * <noscript>日本語解説</noscript> <!-- Description header for when displayed by not-javascript-enabled browser -->
		 * <div id='stringResource1_ja_JP' style='lang:ja-JP;language:JA;mso-ansi-language:JA;mso-fareast-language:JA'>
		 * 日本語のストリング・リソース</div>
		 * <noscript>English description</noscript> <!-- Description header for when displayed by not-javascript-enabled browser -->
		 * <div id='stringResource1_en' style='lang:en;'>
		 * English String resource</div>
		 * <!-- If you don't want to show it on not-javascript-enabled browser, then display style can be set to none -->
		 * <div id='stringResource1_en_US' style='lang:en-US;' display:none;'>
		 * English String resource for US</div>
		 * <!-- Providing id in host html and specifying it in accessing via method yield more quicker processing for string resource -->
		 * <div id='stringResource1_en_UK' style='lang:en-UK; display:none;'>
		 * English String resource for UK</div>
		 * <div id='stringResource1_en_UK' style='lang:en-UK; display:none;'>
		 * Duplicate ID should not happen, and will be ignored</div>
		 * <!-- id of child node can be omitted. -->
		 * <div style='lang:en-CA; display:none;'>
		 * English String resource for CA</div>
		 * </div>
		 * 
		 */

		
		boolean keyWithLangCodeFlag = _instance.isKeyWithLangCode( keyCopy);
		if ( !keyWithLangCodeFlag) {
			if ( !genericDefaultLocaleFlag) {
				keyCopy = keyCopy + "_" + localeNameStr;
			}
		}
GWT.log( "keyWithLangCodeFlag = " + keyWithLangCodeFlag, null);
GWT.log( "keyCopy = " + keyCopy, null);
		
		String valueStr = null;
		Element resourceElementInDictionary = ResourceDeliverDictionary.getValue( keyCopy);
		if ( resourceElementInDictionary != null) {
			switch( contentAccessMeans) {
				case InnerHTML:
					valueStr = resourceElementInDictionary.getInnerHTML();
					break;
				case InnerText:
					valueStr = resourceElementInDictionary.getInnerText();
					break;
				default:
					valueStr = resourceElementInDictionary.getInnerText();
					break;
			} // switch
			if ( ( valueStr != null) && ( !"".equals( valueStr))) {
				if ( asEscaped) {
					valueStr = SimpleHtmlSanitizer.sanitizeHtml( valueStr).asString();
				}
				return valueStr;
			}
		}
		
		Element keyDomElement = DOM.getElementById( keyCopy);
		if ( keyDomElement != null) {
			if ( !keyWithLangCodeFlag && genericDefaultLocaleFlag) { // key doesn't have lang & country codes suffix, current locale is "default"
				// get innerHTML of first child div node
				Element resourceElement = _instance.getFirstDivElement( keyDomElement, keyCopy);
				ResourceDeliverDictionary.add( keyCopy, resourceElement);
				switch( contentAccessMeans) {
					case InnerHTML:
						valueStr = resourceElement.getInnerHTML();
						break; // switch
					case InnerText:
						valueStr = resourceElement.getInnerText();
						break; // switch
					default:
						valueStr = resourceElement.getInnerText();
						break; // switch
				} // switch
			}
			else {
				ResourceDeliverDictionary.add( keyCopy, keyDomElement);
				switch( contentAccessMeans) {
					case InnerHTML:
						valueStr = keyDomElement.getInnerHTML();
						break; // switch
					case InnerText:
						valueStr = keyDomElement.getInnerText();
						break; // switch
					default:
						valueStr = keyDomElement.getInnerText();
						break; // switch
				} // switch
			}
			
			if ( asEscaped) {
				valueStr = SimpleHtmlSanitizer.sanitizeHtml( valueStr).asString();
			}
			return valueStr;
		}
		else { // Could not locate div node of which id is the value of keyCopy 
			if ( !keyWithLangCodeFlag && genericDefaultLocaleFlag) { // key doesn't have lang & country codes suffix, current locale is "default", and couldn't locate node of which id is same with value of keyCopy
				throw new MissingResourceException( 
						"Could not find the resource group div node identified by ID:" + keyCopy, 
						null, keyCopy);
			}
			/* else { // Go through to below section
			} */
		}
		
		// Locate div DOM node wrapping resources 
		Element wrapperDOMElement = _instance.getWrapperDomElement( key, keyWithLangCodeFlag);
		
		// Create language code ladder for fall back 
		LinkedHashMap<String, Element> languageFallbackLadderLinkedHashMap = 
			_instance.createFallBackLanguageLadder( keyCopy, keyWithLangCodeFlag);

		// Save div DOM element for each key in language code ladder for fall back
		// and also get the first div DOM child element as resource of ulitimate fall back
		Element ultimateFallBackElement = 
			_instance.fillDomElementInLangLadder( wrapperDOMElement, languageFallbackLadderLinkedHashMap);
		
		// ----------------------------------------------------------------------------------------
		ArrayList<String> arrayList = new ArrayList<String>( languageFallbackLadderLinkedHashMap.keySet());
		ListIterator<String> listIterator = arrayList.listIterator();
		valueStr = "";
		Element element = null;
		while( listIterator.hasNext()) {
			element = languageFallbackLadderLinkedHashMap.get( listIterator.next());
			if ( element != null) {
				ResourceDeliverDictionary.add( keyCopy, element);
				switch( contentAccessMeans) {
					case InnerHTML:
						valueStr = element.getInnerHTML();
						break; // switch
					case InnerText:
						valueStr = element.getInnerText();
						break; // switch
					default:
						valueStr = element.getInnerText();
						break; // switch
				} // switch
				
				break; // while
			}
		} // while
		
		if ( "".equals( valueStr)) {
			if ( ultimateFallBackElement == null) {
				throw new MissingResourceException( 
						"Could not find the resource for specific locale nor fall back locale(s) at all.", null, keyCopy);
			}
			else {
				switch( contentAccessMeans) {
					case InnerHTML:
						valueStr = ultimateFallBackElement.getInnerHTML();
						break;
					case InnerText:
						valueStr = ultimateFallBackElement.getInnerText();
						break;
					default:
						valueStr = ultimateFallBackElement.getInnerText();
						break;
				} // switch
			}
		}
		if ( ( valueStr == null) || ( "".equals( valueStr))) {
			throw new MissingResourceException( "Could not find the resource for specific locale nor fall back locale(s) at all.", null, keyCopy);
		}
		// ----------------------------------------------------------------------------------------
		
		if ( asEscaped) {
			valueStr = SimpleHtmlSanitizer.sanitizeHtml( valueStr).asString();
		}
		return valueStr;
	} // public static String getValue( String key) 
	
	public static Element getResourceAsElement( String key) throws IllegalArgumentException {
		String keyCopy = _instance.keyValidator( key);
		
		boolean keyWithLangCodeFlag = _instance.isKeyWithLangCode( keyCopy);
		if ( !keyWithLangCodeFlag) {
			if ( !genericDefaultLocaleFlag) {
				keyCopy = keyCopy + "_" + localeNameStr;
			}
		}
GWT.log( "keyWithLangCodeFlag = " + keyWithLangCodeFlag, null);
GWT.log( "keyCopy = " + keyCopy, null);
		
		Element resourceAsElement = ResourceDeliverDictionary.getValue( keyCopy);
		if ( resourceAsElement != null) return resourceAsElement;
		
		Element keyDomElement = DOM.getElementById( keyCopy);
		if ( keyDomElement != null) {
			if ( !keyWithLangCodeFlag && genericDefaultLocaleFlag) { // key doesn't have lang & country codes suffix, current locale is "default"
				// get innerHTML of first child div node
				keyDomElement = _instance.getFirstDivElement( keyDomElement, keyCopy);
				ResourceDeliverDictionary.add( keyCopy, keyDomElement);
			}
			else {
				ResourceDeliverDictionary.add( keyCopy, keyDomElement);
			}
			
			return keyDomElement;
		}
		else { // Could not locate div node of which id is the value of keyCopy 
			if ( !keyWithLangCodeFlag && genericDefaultLocaleFlag) { // key doesn't have lang & country codes suffix, current locale is "default", and couldn't locate node of which id is same with value of keyCopy
				throw new MissingResourceException( 
						"Could not find the resource group div node identified by ID:" + keyCopy, 
						null, keyCopy);
			}
			/* else { // Go through to below section
			} */
		}

		// Locate div DOM node wrapping resources 
		Element wrapperDOMElement = _instance.getWrapperDomElement( key, keyWithLangCodeFlag);		
		
		// Create language code ladder for fall back 
		LinkedHashMap<String, Element> languageFallbackLadderLinkedHashMap = 
			_instance.createFallBackLanguageLadder( keyCopy, keyWithLangCodeFlag);
		
		// Save innerHTML value for each key in language code ladder for fall back 
		// and also get the first div DOM child element as resource of ulitimate fall back
		Element ultimateFallBackElement = 
			_instance.fillDomElementInLangLadder( wrapperDOMElement, languageFallbackLadderLinkedHashMap);

		// ----------------------------------------------------------------------------------------
		ArrayList<String> arrayList = new ArrayList<String>( languageFallbackLadderLinkedHashMap.keySet());
		ListIterator<String> listIterator = arrayList.listIterator();
		Element element = null;
		while( listIterator.hasNext()) {
			element = languageFallbackLadderLinkedHashMap.get( listIterator.next());
			if ( element != null) {
				ResourceDeliverDictionary.add( keyCopy, element);
				break; // while
			}
		} // while
		
		if ( element == null) {
			if ( ultimateFallBackElement == null) {
				throw new MissingResourceException( 
						"Could not find the resource for specific locale nor fall back locale(s) at all.", null, keyCopy);
			}
			else {
				element = ultimateFallBackElement;
			}
		}
		// ----------------------------------------------------------------------------------------
		
		return element;
	} // public Element getResourceAsElement( String key)
}
